package com.iwhalecloud.bss.kite.cucc.order.proxy;

import com.iwhalecloud.bss.kite.common.constant.ErrorConsts;
import com.iwhalecloud.bss.kite.common.constant.TableConsts;
import com.iwhalecloud.bss.kite.common.util.KiteBeanUtils;
import com.iwhalecloud.bss.kite.common.util.KiteDateUtils;
import com.iwhalecloud.bss.kite.common.util.KiteStringUtils;
import com.iwhalecloud.bss.kite.cucc.order.util.AcceptUtils;
import com.iwhalecloud.bss.kite.cucc.order.util.InstProxyUtils;
import com.iwhalecloud.bss.kite.cucc.order.util.TableUtils;
import com.iwhalecloud.bss.kite.dataservice.entity.inst.IInst;
import com.iwhalecloud.bss.kite.dataservice.entity.inst.OfferInstAttr;
import com.iwhalecloud.bss.kite.dataservice.entity.inst.ProdInstFuncExt;
import com.iwhalecloud.bss.kite.dataservice.enums.TableConfig;
import com.iwhalecloud.bss.kite.order.client.context.OrderContext;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;

import javax.persistence.Column;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Objects;

/**
 * 实例动态代理<br>
 * <li>生成订单后<--->保存前如果需要修改实例的属性，需要补充orderItemChange变动信息
 * <li>如果实例原来就有属性变动信息，才会把新的属性变动信息补充进来(如果实例没有变动信息，生成变动信息时还要找相应的orderItemId)
 * @param <T>
 */
public class InstProxy<T extends IInst> implements MethodInterceptor {

    private T target;

    /**
     * 创建代理实例
     * @param target
     * @return
     */
    @SuppressWarnings("unchecked")
    public T createProxy(T target) {
        if (Objects.isNull(target)) {
            return target;
        }

        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        enhancer.setClassLoader(target.getClass().getClassLoader());
        return (T) enhancer.create();
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result = methodProxy.invokeSuper(proxy, args);
        doAfter(proxy, method, args);
        return result;
    }

    /**
     * 执行后处理方法
     * @param proxy
     * @param method
     * @param args
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    private void doAfter(Object proxy, Method method, Object[] args)
        throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if (!ObjectUtils.allNotNull(proxy, method) || !method.getName().startsWith("set") || ArrayUtils.isEmpty(args)) {
            return;
        }

        Class<?> targetClass = proxy.getClass().getSuperclass();
        String fieldName = getFieldName(method);
        PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(targetClass, fieldName);
        if (Objects.isNull(propertyDescriptor)) {
            return;
        }

        Object newValue = args[0];
        Object oldValue = propertyDescriptor.getReadMethod().invoke(target);
        // 修改原实例的属性值
        propertyDescriptor.getWriteMethod().invoke(target, args);

        if (Objects.isNull(OrderContext.getOrderRequest())) {
            return;
        }

        if (target instanceof OfferInstAttr || target instanceof ProdInstFuncExt) {
            // OfferInstAttr和ProdInstFuncExt是纵表，特殊处理
            if (Objects.equals("attrValue", fieldName)) {
                String tableName = "";
                String instId = "";
                String colName = "";
                String oldString = (String) oldValue;
                String newString = (String) newValue;
                if (target instanceof OfferInstAttr) {
                    OfferInstAttr offerInstAttr = (OfferInstAttr) target;
                    tableName = TableConsts.OfferInst.getTableName();
                    instId = offerInstAttr.getOfferInstId();
                    colName = offerInstAttr.getFieldName();
                }
                else if (target instanceof ProdInstFuncExt) {
                    ProdInstFuncExt prodInstFuncExt = (ProdInstFuncExt) target;
                    tableName = TableConsts.ProdInstFunc.getTableName();
                    instId = prodInstFuncExt.getProdInstId();
                    colName = prodInstFuncExt.getFieldName();
                }

                InstProxyUtils.createOrderItemChange(tableName, instId, colName, newString, oldString);
            }
        }
        else {
            // (actionType == A && newActionType == M) || actionType == M
            if (!AcceptUtils.isModifyInst(target)) {
                // 修改的实例才需要生成属性变动信息
                return;
            }

            String tableName = TableUtils.getTableName(targetClass);
            String colName = getColName(targetClass, fieldName);
            if (StringUtils.isAnyBlank(tableName, colName)) {
                return;
            }

            String oldString = "";
            String newString = "";
            // 只支持String、Date、LocalDateTime类型
            if (Objects.equals(String.class, propertyDescriptor.getPropertyType())) {
                oldString = (String) oldValue;
                newString = (String) newValue;
            }
            else if (Objects.equals(Date.class, propertyDescriptor.getPropertyType())) {
                oldString = KiteDateUtils.formatDate((Date) oldValue, KiteDateUtils.DATE_TIME_FORMAT);
                newString = KiteDateUtils.formatDate((Date) newValue, KiteDateUtils.DATE_TIME_FORMAT);
            }
            else if (Objects.equals(LocalDateTime.class, propertyDescriptor.getPropertyType())) {
                oldString = KiteDateUtils.formatLocalDateTime((LocalDateTime) oldValue, KiteDateUtils.DATE_TIME_FORMAT);
                newString = KiteDateUtils.formatLocalDateTime((LocalDateTime) newValue, KiteDateUtils.DATE_TIME_FORMAT);
            }

            if (!KiteStringUtils.equals(oldString, newString)) {
                TableConfig tableConfig = EnumUtils.getEnum(TableConfig.class, tableName);
                if (Objects.isNull(tableConfig)) {
                    ErrorConsts.BUSI_COMMON_ERROR.throwOut("no table config [TableConfig." + tableName + "]");
                }

                String idField = tableConfig.getIdField();
                String instId = KiteBeanUtils.getValueByName(target, idField);
                InstProxyUtils.createOrderItemChange(tableName, instId, colName, newString, oldString);
            }
        }
    }

    /**
     * 根据get/set方法获取属性名
     * @param method
     * @return
     */
    private String getFieldName(Method method) {
        if (Objects.isNull(method) || !(method.getName().startsWith("set") || method.getName().startsWith("get"))) {
            return "";
        }

        return StringUtils.uncapitalize(method.getName().substring(3));
    }

    /**
     * 把属性名转换为数据字段名(下划线格式)
     * @param clazz
     * @param method
     * @return
     */
    private String getColName(Class<?> clazz, String fieldName) {
        if (Objects.isNull(clazz) || StringUtils.isBlank(fieldName)) {
            return "";
        }

        Field field = findField(clazz, fieldName);
        if (Objects.isNull(field)) {
            return "";
        }

        String colName = "";
        Column column = field.getAnnotation(Column.class);
        if (Objects.nonNull(column)) {
            colName = column.name();
        }

        if (StringUtils.isBlank(colName)) {
            colName = KiteStringUtils.camelToUnderline(fieldName);
        }

        return colName;
    }

    /**
     * 根据属性名获取类属性对象
     * @param clazz
     * @param name
     * @return
     */
    private Field findField(Class<?> clazz, String name) {
        if (Objects.isNull(clazz) || StringUtils.isBlank(name)) {
            return null;
        }

        Class<?> searchType = clazz;
        while (Object.class != searchType && searchType != null) {
            Field field = null;
            try {
                field = searchType.getDeclaredField(name);
            }
            catch (NoSuchFieldException | SecurityException e) {
                // ignore
            }
            if (Objects.nonNull(field)) {
                return field;
            }
            searchType = searchType.getSuperclass();
        }

        return null;
    }
}
